from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.conf import settings
from django.utils import timezone
from minio import Minio
import hashlib
import os
import mimetypes
import io
import json
import re
import uuid
import time
import base64
import hmac
from system.models import File
from .files_serializers import FileInfoSerializer


class FileUploadView(APIView):
    """文件上传视图"""
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        """
        上传文件接口
        """
        # 获取上传的文件
        upload_file = request.FILES.get('file')
        if not upload_file:
            return Response({"code": "400", "msg": "未接收到文件"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            # 初始化MinIO客户端
            minio_client = Minio(
                settings.MINIO_HOST_PORT,
                access_key=settings.MINIO_ACCESS_KEY,
                secret_key=settings.MINIO_SECRET_KEY,
                secure=settings.MINIO_SSL
            )

            # 确保存储桶存在
            bucket_name = settings.MINIO_BUCKET_NAME
            if not minio_client.bucket_exists(bucket_name):
                minio_client.make_bucket(bucket_name)
                # 新建存储桶时设置公开访问策略
                self._set_bucket_public_policy(minio_client, bucket_name)
            elif not self._check_bucket_has_public_policy(minio_client, bucket_name):
                # 如果存储桶已存在但没有公开访问策略，设置公开访问策略
                self._set_bucket_public_policy(minio_client, bucket_name)

            # 读取文件内容到内存中，避免多次消耗文件流
            file_content = upload_file.read()

            # 计算文件MD5值
            md5_hash = hashlib.md5()
            md5_hash.update(file_content)
            md5_code = md5_hash.hexdigest()

            # 生成安全签名
            timestamp = str(int(time.time()))
            random_str = str(uuid.uuid4()).replace('-', '')[:12]
            
            # 使用HMAC生成签名（结合时间戳、随机字符串和文件MD5）
            signature_data = f"{md5_code}_{timestamp}_{random_str}"
            signature = hmac.new(
                settings.SECRET_KEY.encode('utf-8'),
                signature_data.encode('utf-8'),
                hashlib.sha256
            ).hexdigest()[:16]  # 取16位签名
            
            # 安全签名部分
            security_part = f"{timestamp}{random_str}{signature}"
            
            # 确定存储路径，使用MD5的前两个字符作为目录，并加入安全签名
            first_char = md5_code[0]
            second_char = md5_code[1]
            file_name = upload_file.name
            storage_path = f"{first_char}/{second_char}/{md5_code}__{security_part}__{file_name}"

            # 获取文件类型
            content_type, _ = mimetypes.guess_type(file_name)
            if content_type is None:
                content_type = 'application/octet-stream'

            # 文件大小
            file_size = len(file_content)

            # 上传文件到MinIO (使用BytesIO将内存中的内容转换为文件对象)
            minio_client.put_object(
                bucket_name,
                storage_path,
                io.BytesIO(file_content),
                file_size,
                content_type=content_type
            )

            # 使用timezone.now()获取带时区的当前时间
            current_time = timezone.now()
            
            # 保存文件信息到数据库
            file_record = File(
                file_name=file_name,
                file_path=storage_path,
                file_size=file_size,
                file_type=content_type,
                create_time=current_time,
                create_by=request.user.id
            )
            file_record.save()

            # 构建文件URL
            protocol = 'https' if settings.MINIO_SSL else 'http'
            url = f"{protocol}://{settings.MINIO_HOST_PORT}/{bucket_name}/{storage_path}"

            # 返回结果
            file_info = {
                'file_name': file_name,
                'url': url
            }
            serializer = FileInfoSerializer(file_info)
            return Response({"code": "00000", "data": serializer.data, "msg": "文件上传成功"}, status=status.HTTP_200_OK)

        except Exception as e:
            return Response({"code": "500", "msg": f"文件上传失败: {str(e)}"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
    def delete(self, request):
        """
        删除文件接口（逻辑删除）
        """
        # 获取文件路径参数
        file_path_or_url = request.query_params.get('filePath')
        if not file_path_or_url:
            return Response({"code": "400", "msg": "未提供文件路径"}, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            # 处理URL格式
            file_path = self._extract_file_path(file_path_or_url)
            if not file_path:
                return Response({"code": "400", "msg": "无效的文件路径或URL"}, status=status.HTTP_400_BAD_REQUEST)
            
            # 查找对应的文件记录
            file_record = File.objects.filter(file_path=file_path, is_deleted=0).first()
            if not file_record:
                return Response({"code": "404", "msg": "文件不存在或已删除"}, status=status.HTTP_404_NOT_FOUND)
            
            # 使用timezone.now()获取带时区的当前时间
            current_time = timezone.now()
            
            # 逻辑删除文件记录
            file_record.is_deleted = 1
            file_record.delete_by = request.user.id
            file_record.delete_time = current_time
            file_record.save()
            
            return Response({"code": "00000", "data": {}, "msg": "文件删除成功"}, status=status.HTTP_200_OK)
            
        except Exception as e:
            return Response({"code": "500", "msg": f"文件删除失败: {str(e)}"}, 
                           status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
    def _extract_file_path(self, path_or_url):
        """
        从文件路径或URL中提取文件路径
        
        支持的格式:
        1. 直接文件路径: a/b/file.jpg
        2. 完整URL: http://server:port/bucket/a/b/file.jpg
        """
        # 检查是否是URL格式
        if path_or_url.startswith(('http://', 'https://')):
            # 使用正则表达式提取路径部分
            # 匹配格式: http://server:port/bucket/path
            bucket_name = settings.MINIO_BUCKET_NAME
            pattern = f"(?:https?://[^/]+/{bucket_name}/)(.+)"
            match = re.search(pattern, path_or_url)
            if match:
                return match.group(1)
            return None
        
        # 如果不是URL格式，则直接返回
        return path_or_url
    
    def _set_bucket_public_policy(self, minio_client, bucket_name):
        """设置存储桶的公开访问策略"""
        # 定义桶策略 - 允许所有用户获取此桶中的所有对象
        policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"AWS": "*"},  # 允许所有用户
                    "Action": ["s3:GetObject"],  # 只允许获取对象操作
                    "Resource": [f"arn:aws:s3:::{bucket_name}/*"]  # 应用于桶中的所有对象
                }
            ]
        }
        policy_json = json.dumps(policy)
        minio_client.set_bucket_policy(bucket_name, policy_json)
    
    def _check_bucket_has_public_policy(self, minio_client, bucket_name):
        """检查存储桶是否已有公开访问策略"""
        try:
            # 获取当前桶策略
            policy = minio_client.get_bucket_policy(bucket_name)
            # 检查策略是否包含公开访问所需的配置
            policy_dict = json.loads(policy)
            for statement in policy_dict.get("Statement", []):
                if (statement.get("Effect") == "Allow" and 
                    statement.get("Principal", {}).get("AWS") == "*" and
                    "s3:GetObject" in statement.get("Action", [])):
                    return True
            return False
        except Exception:
            # 如果获取策略失败，可能是因为没有设置策略
            return False
